/*******************************************************************************
 * Copyright (c) 2011 Irineu Martins de Lima Moura (imlm2). This file is part of
 * JBeat. JBeat is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version. JBeat is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * for more details. You should have received a copy of the GNU Lesser General
 * Public License along with JBeat. If not, see <http://www.gnu.org/licenses/>.
 * Contributors: Irineu Martins de Lima Moura (imlm2) - initial API and
 * implementation
 ******************************************************************************/
package br.ufpe.cin.imlm2.jbeat.soot.throwableset.generation;

import java.util.HashSet;
import java.util.Set;

import soot.Body;
import soot.NullType;
import soot.RefType;
import soot.SootClass;
import soot.SootMethod;
import soot.Type;
import soot.Unit;
import soot.UnknownType;
import soot.Value;
import soot.jimple.Stmt;
import soot.jimple.ThrowStmt;
import soot.tagkit.Host;
import soot.util.Chain;
import br.ufpe.cin.imlm2.jbeat.soot.common.CommonExceptions;
import br.ufpe.cin.imlm2.jbeat.soot.throwableset.ThrowableSetTagger;
import br.ufpe.cin.imlm2.jbeat.soot.throwableset.tags.ThrowableSetTag;
import br.ufpe.cin.imlm2.jbeat.soot.traps.TrapManager;

public class RaisesCalculator extends ThrowableSetTagger {

	@Override
	protected void tag() {
		super.tag();
		for ( Host h : this.getTargetSet() ) {
			SootMethod meth = (SootMethod) h;
			Set<SootClass> exceptions = this.visitInstructions( meth );
			ThrowableSetTag tst = (ThrowableSetTag) meth.getTag( ThrowableSetTag.TAG_NAME );
			tst.addAllThrownExceptions( this.filterExceptions( exceptions, meth ) );
		}
	}

	protected Set<SootClass> visitInstructions( SootMethod sootMethod ) {
		Body b = sootMethod.retrieveActiveBody();
		Chain<Unit> stmts = b.getUnits();
		Set<SootClass> thrownSet = new HashSet<SootClass>();
		for ( Unit u : stmts ) {
			Stmt stmt = (Stmt) u;
			if ( stmt instanceof ThrowStmt ) {
				SootClass raised = this.raisedException( (ThrowStmt) stmt );
				if ( !TrapManager.isExceptionCaughtAt( raised, stmt, b ) ) {
					thrownSet.add( raised );
				}
			}
		}
		return thrownSet;
	}

	protected SootClass raisedException( ThrowStmt t ) {
		Value thrownExpression = t.getOp();
		Type thrownType = thrownExpression.getType();
		if ( thrownType == null || thrownType instanceof UnknownType ) {
			// We can't identify the type of thrownExpression, so...
			return CommonExceptions.getTHROWABLE();
		} else if ( thrownType instanceof NullType ) {
			return CommonExceptions.getNULLPOINTER();
		} else if ( !(thrownType instanceof RefType) ) {
			throw new RuntimeException( "Type of throw argument is not a RefType!" );
		} else {
			return ((RefType) thrownType).getSootClass();
		}
	}

}
